﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
//using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.PlatformAbstractions;
using Microsoft.Extensions.Logging;
using Swashbuckle.Swagger.Model;
using System.IO;
using Hangfire;
using HuntingFishGame.OpenApi.Filters;
using Microsoft.AspNetCore.Mvc.Filters;
using StackExchange.Redis;

namespace HuntingFishGame.OpenApi
{
    /// <summary>
    /// ASP.NET Core 1.0 中使用 Swagger 生成文档 http://www.cnblogs.com/Irving/p/5205298.html
    /// Asp.net core WebApi 使用Swagger生成帮助页 http://www.cnblogs.com/suxinlcq/p/6757556.html
    /// 更多关于Swagger的用法可以参考：https://github.com/domaindrivendev/Swashbuckle.AspNetCore 以及微软文档：https://docs.microsoft.com/zh-cn/aspnet/core/tutorials/web-api-help-pages-using-swagger
    /// netcore
    /// http://www.cnblogs.com/wangrudong003/p/5654325.html
    /// http://www.cnblogs.com/savorboard/p/aspnetcore-identity3.html
    /// 解读ASP.NET 5 & MVC6系列（6）：Middleware详解
    /// http://www.cnblogs.com/TomXu/p/4496435.html
    /// .NETCore使用Dapper操作MySQL 
    /// http://blog.csdn.net/mituan1234567/article/details/53170685
    /// http://www.alonely.com.cn/MySQL/20160825/17389.html
    /// GitHub ：https://github.com/linezero/Blog/tree/master/NETCoreMySQL
    /// Dapper: https://github.com/StackExchange/dapper-dot-net
    /// </summary>
    public class Startup
    {
        /// <summary>
        /// Startup文件中执行方法的步骤使用A，B，C，D表示
        /// </summary>
        /// <param name="env"></param>
        /// <remarks>
        /// 步骤A   IHostingEnvironment获取环境变量信息，没错就是获取环境变量
        /// </remarks>
        public Startup(IHostingEnvironment env)
        {
            //这里创建ConfigurationBuilder，其作用就是加载Congfig等配置文件
            var builder = new ConfigurationBuilder()
                //env.ContentRootPath：获取当前项目的跟路径
                .SetBasePath(env.ContentRootPath)
                //使用AddJsonFile方法把项目中的appsettings.json配置文件加载进来，后面的reloadOnChange顾名思义就是文件如果改动就重新加载
                .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
                //这里关注的是$"{param}"的这种写法，有点类似于string.Format()
                .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
                // adding cache.json which contains cachemanager configuration(s)
                .AddJsonFile("cache.json", optional: false);
            //读取环境变量是否为Development
            if (env.IsDevelopment())
            {
                // This will push telemetry data through Application Insights pipeline faster, allowing you to view results immediately.
                //待研究
                builder.AddApplicationInsightsSettings(developerMode: true);
            }

            builder.AddEnvironmentVariables();
            //这返回一个配置文件跟节点：IConfigurationRoot
            Configuration = builder.Build();
        }
        /// <summary>
        /// 通过构造器获取配置文件等信息（个人眉脚：这里是不是可以把这个节点开放到整个项目都是用，因为有些配置信息需要在实际的代码类中使用到，这样就不用再单独再获取一次了吧）
        /// Microsoft.Extensions.Configuration.IConfiguration 层次结构的根
        /// </summary>
        public IConfigurationRoot Configuration { get; }

        /// <summary>
        /// Redis 服务
        /// </summary>
        public static ConnectionMultiplexer Redis;

        /// <summary>
        /// 步骤B  各种服务依赖注册的地方，netcore还是配置依赖注入方式为主
        /// 这个方法被运行时调用。使用此方法将服务添加到容器中
        /// </summary>
        /// <param name="services"></param>
        public void ConfigureServices(IServiceCollection services)
        {
            //待验证功能
            services.AddApplicationInsightsTelemetry(Configuration);

            //添加mvc框架服务，没有这个mvc运行不起
            services.AddMvc();

            services.AddLogging();

            // Inject an implementation of ISwaggerProvider with defaulted settings applied  
            services.AddSwaggerGen();

            // 添加API的详细信息(Add the detail information for the API)  
            services.ConfigureSwaggerGen(options =>
            //services.ConfigureSwaggerDocument(options =>
            {
                options.SingleApiVersion(new Info
                {
                    Version = "v1",
                    Title = "ASP.NET Core 1.0 WebAPI",
                    Description = "A Simple For ASP.NET Core 1.0 WebAPI",
                    TermsOfService = "None",
                    Contact = new Contact { Name = "irving", Email = "zhouyongtao@outlook.com", Url = @"http://cnblogs.com/irving" },
                    License = new License { Name = "irving", Url = @"http://cnblogs.com/irving" },
                });

                ////确定应用程序的基本路径
                //var basePath = PlatformServices.Default.Application.ApplicationBasePath;

                ////设置注释路径(swagger json and ui)
                //var xmlPath = Path.Combine(basePath, "HuntingFishGame.OpenApi.xml");
                //options.IncludeXmlComments(xmlPath);
            });

            //首先用AddOptions() 初始化注入IOptions<T>，然后Configure<AppSettings>则是自动初始化AppSettings实例并且映射appSettings里的配置
            //Add functionality to inject IOptions<T>
            services.AddOptions();
            services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
            //返回大小写问题
            //http://www.cnblogs.com/Irving/p/6884760.html
            //http://www.cnblogs.com/Leo_wl/p/5805287.html
            //http://www.cnblogs.com/chenug/p/6655636.html
            services.AddMvc().AddJsonOptions(option => option.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver());

            //注入Hnagfire服务
            services.AddHangfire(r => r.UseSqlServerStorage(Configuration.GetConnectionString("HangfireConnection")));
            //注入Redis服务
            Redis = ConnectionMultiplexer.Connect(Configuration.GetConnectionString("RedisConnnection"));
            services.AddHangfire(config => config.UseRedisStorage(Redis));

            //这里是注入了数据库上下文，如果没有这里，刚才的Controller获取出来根本就获取不到数据信息
            //Configuration.GetConnectionString("DefaultConnection")获取了配置文件appsettings.json中的DefaultConnection节点数据
            //注：因为ConfigurationBuilder的作用，咋们以后获取json配置文件时候可以使用如appsettings.json文件中的：Configuration.GetSection("Logging:LogLevel:Microsoft"); 方法，获取json层级关系的属性值
            //services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("HangfireConnection")));


            ////注入Indentity，目前这个IdentityRole在这里暂时没用到
            //services.AddIdentity<ApplicationUser, IdentityRole>(options => {
            //        options.Cookies.ApplicationCookie.AuthenticationScheme = "ApplicationCookie";
            //        options.Cookies.ApplicationCookie.CookieName = "Interop";
            //    })
            //    .AddEntityFrameworkStores<ApplicationDbContext>()
            //    .AddDefaultTokenProviders();
            ////注入netcore自带的两个服务
            //services.AddTransient<IEmailSender, AuthMessageSender>();
            //services.AddTransient<ISmsSender, AuthMessageSender>();
            //这里还有其他注入方式
            //services.AddSingleton<>();//单例注入
            //services.AddScoped<>();//作用域注入
            //services.AddMemoryCache(); //MemoryCache缓存注入
        }

        /// <summary>
        /// 步骤C
        /// 这个方法被运行时调用。使用此方法配置HTTP请求管道。
        /// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        /// </summary>
        /// <param name="app"></param>
        /// <param name="env"></param>
        /// <param name="loggerFactory"></param>
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            app.Use(new Func<Microsoft.AspNetCore.Http.RequestDelegate, Microsoft.AspNetCore.Http.RequestDelegate>(next => content => Invoke(next, content)));
            // 添加日志支持,添加Console输出
            //loggerFactory.AddConsole();
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            // 添加NLog日志支持
            //loggerFactory.AddNLog();

            // 添加自定义中间件
            //app.UseTimeRecorderMiddleware(); // 要放在前面，以便进行统计，如果放在Mvc后面的话，就统计不了时间了。
            //app.Use(next => new BSF.EFCoreFramework.TimeRecorderMiddleware(next).Invoke);
            //app.UseMiddleware<TimeRecorderMiddleware>();
            ////app.UseMiddleware(typeof(TimeRecorderMiddleware));
            //app.MapWhen(context =>
            //{
            //    return context.Request.Query.ContainsKey("a");
            //}, MapTest);
            app.Map("/test", MapTest);

            // give some error details in debug mode
            if (env.IsDevelopment())
            {
                app.Use(async (ctx, next) =>
                {
                    try
                    {
                        await next.Invoke();
                    }
                    catch (Exception ex)
                    {
                        await ctx.Response.WriteAsync($"{{\"error\": \"{ex}\"}}");
                    }
                    //跳转到异常页面，异常错误展示
                    app.UseDeveloperExceptionPage();
                    //app.UseDatabaseErrorPage();
                    //app.UseBrowserLink();
                });
            }
            else
            {
                //如果错误，从定向到错误改路由,捕获所有的程序异常错误，并将请求跳转至指定的页面，以达到友好提示的目的。
                app.UseExceptionHandler("/Home/Error");
            }

            // lets redirect to the swagger ui, there is nothing else to display otherwise ;)
            app.Use(async (ctx, next) =>
            {
                if (ctx.Request.Path.StartsWithSegments("/"))
                {
                    ctx.Response.Redirect("/swagger/ui");
                }
                else
                {
                    await next.Invoke();
                }
            });

            //启用静态文件中间件,开启静态文件也能走该Pipeline管线处理流程的功能
            //Add static files to the request pipeline.
            app.UseStaticFiles();

            //使用了 CookieAuthentication 中间件做身份认证,开启以cookie为基础的ASP.NET identity认证功能，以支持Pipeline请求处理。
            ConfigureCookie(app);

            //http://www.cnblogs.com/chenug/p/6655636.html
            //http://www.cnblogs.com/Leo_wl/p/5805287.html
            //http://www.cnblogs.com/Irving/p/6884760.html
            app.UseHangfireServer();//启动Hangfire服务
            var jobOptions = new BackgroundJobServerOptions
            {
                Queues = new[] { "test", "default" },//队列名称，只能为小写
                WorkerCount = Environment.ProcessorCount * 5, //并发任务数
                ServerName = "hangfire1",//服务器名称
            };
            app.UseHangfireServer(jobOptions);
            //启动hangfire面板
            var options = new DashboardOptions
            {
                Authorization = new[] { new HangfireAuthorizationFilter() }
            };
            app.UseHangfireDashboard("/hangfire", options);



            // 添加MVC中间件
            //app.UseMvc();
            app.UseMvcWithDefaultRoute();
            //设置mvc路由
            // Add MVC to the request pipeline.
            //app.UseMvc(routes =>
            //{
            //    //这里的{id?} ？号表示可以有可无
            //    routes.MapRoute(
            //        name: "default",
            //        template: "{controller=Home}/{action=Index}/{id?}");
            //});

            //将Swagger加入到.net core的中间件Middleware中
            // Enable middleware to serve generated Swagger as a JSON endpoint.
            app.UseSwagger();

            // Enable middleware to serve swagger-ui assets (HTML, JS, CSS etc.)  
            app.UseSwaggerUi();

        }

        private static void ConfigureCookie(IApplicationBuilder app)
        {
            ////使用了 CookieAuthentication 中间件做身份认证,开启以cookie为基础的ASP.NET identity认证功能，以支持Pipeline请求处理。
            ////Add cookie-based authentication to the request pipeline.
            //app.UseIdentity();
        }

        //public void ConfigureAuth(IAppBuilder app)
        //{
        //    app.CreatePerOwinContext(ApplicationDbContext.Create);
        //    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
        //    //省略,先不解释
        //}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="next"></param>
        /// <param name="content"></param>
        /// <returns></returns>
        /// <remarks>
        /// 注意Invoke方法的参数
        /// </remarks>
        private async Task Invoke(Microsoft.AspNetCore.Http.RequestDelegate next, HttpContext content)
        {
            Console.WriteLine("初始化组件开始");
            await next.Invoke(content);
            Console.WriteLine("管道下步执行完毕");
        }

        /// <summary>
        /// 扩展方法用于注册该Middleware
        /// </summary>
        /// <param name="app"></param>
        /// <returns></returns>
        //public static IApplicationBuilder UseTimeRecorderMiddleware(this IApplicationBuilder app)
        //{
        //    return app.UseMiddleware<TimeRecorderMiddleware>();
        //}

        private static void MapTest(IApplicationBuilder app)
        {
            //app.Run(async context =>
            //{
            //    //await context.Response.WriteAsync("Url is " + context.Request.PathBase.ToString());
            //    await context.Response.WriteAsync($"Url is {context.Request.Path.ToString()}{context.Request.QueryString.Value}");
            //});
        }

    }

}
